#include "../include/rpc_client.h"
#include "../include/json_serializer.h"

#include <muduo/base/Logging.h>
#include <mutex>
#include <condition_variable>

// 私有方法
// 处理与服务器的连接事件
void scRpc::RpcClient::OnConnection(const muduo::net::TcpConnectionPtr& conn) {
	// 连接成功 保存连接对象
	// 输出成功日志
	if (conn_->connected()) {
		conn_ = conn;
		LOG_INFO << "Connected to server";
	} else {
		conn_.reset();		// 重置
		LOG_INFO << "Disconnected from server";  // 输出连接断开日志
	}
}

// 处理接收到的信息
// 消息回调的具体实现
void scRpc::RpcClient::OnMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp time) {
	// 获取收到的消息内容
	std::string msg(buf->retrieveAllAsString());
	// 输出收到的信息
	LOG_DEBUG << "Received: " << msg;

	// 收到信息反序列化为 RpcResponse 对象
	RpcResponse response;
	if (JsonSerializer::Deserialize(msg, response)) {
		std::shared_ptr<ResponseWaiter> waiter;
		{
			// 查找该响应对应的waiter对象
			std::lock_guard<std::mutex> lock(waitersMutex_);
			// find 查找 迭代器也可 多种方式
			auto it = waiters_.find(response.id);
			if (it != waiters_.end()) {
				waiter = it->second;
				waiters_.erase(it);		// 找到了就将 该waiter对象 从waiters中移除
			}
		}
		// 找到的对应waiter对象
		if (waiter) {
			// 加锁
			std::lock_guard<std::mutex> lock(waiter->mtx);
			// 响应错误的话  错误信息给ans
			if (!response.error.empty())	waiter->ans = response.error;
			else 			waiter->ans = response.ans;

			// 收到响应 给标记 
			waiter->received = true;
			waiter->cv.notify_one();		// 唤醒等待线程
		}
	}
}

// 构造函数，初始化RpcClient实例
// loop：事件循环，负责处理异步事件
// serverAddr：服务器地址，表示目标RPC服务器的地址和端口
scRpc::RpcClient::RpcClient(muduo::net::EventLoop* loop, const muduo::net::InetAddress& serverAddr) : client_(loop, serverAddr, "RpcClient") {
	// 初始化列表 用于构造  传入事件循环和服务器地址

	// 设置连接回调函数，连接建立时调用OnConnection
	client_.setConnectionCallback(std::bind(&RpcClient::OnConnection, this, std::placeholders::_1));

	// 消息回调 调用OnMessage
	client_.setMessageCallback(std::bind(&RpcClient::OnMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

	// 自动重试 连接失败就自动重试
	client_.enableRetry();
}

// 发起Rpc 请求 调用方法 传参
// method：请求的RPC方法名
// params：传递给RPC方法的参数
// timeoutMs：请求超时时间，默认值为3000毫秒
nlohmann::json scRpc::RpcClient::Call(const std::string& method, const nlohmann::json& params, int timeoutMs) {
	// 连接还未建立 尝试ping 服务器
	if (!conn_) {
		client_.connect();
		// 连接等待 10次 每次100ms
		for (int i = 0; i < 10 && !conn_; i++) {
			usleep(100000);
		}
		if (!conn_) {
            throw std::runtime_error("Connect to server failed"); // 如果10次连接失败，抛出异常
        }
	}

	// 创建 请求实例对象
	RpcRequest request;
	request.method = method;
	request.params = params;
	request.id = ++curId_;

	// 封装waiter 
	// ResponseWaiter对象用于同步等待响应
	auto waiter = std::make_shared<ResponseWaiter>();
	{
		// 注意生命周期
		std::lock_guard<std::mutex> lock(waitersMutex_);
		waiters_[request.id] = waiter;		// 将该waiter对象加入到请求ID的waiters映射中
	}

	// 序列化请求 实例对象 string
	std::string msg = JsonSerializer::Serialize(request);
	// 连接方式来发送请求
	conn_->send(msg);

	// 使用mutex和condition_variable来等待响应
	std::unique_lock<std::mutex> lock(waiter->mtx);
	if (!waiter->cv.wait_for(lock, std::chrono::milliseconds(timeoutMs), [&] { return waiter->received; })) {
		// 如果超时，则从waiters中移除该请求
		std::lock_guard<std::mutex> lock(waitersMutex_);
        waiters_.erase(request.id);
        throw std::runtime_error("RPC call timeout");
	}

	return waiter->ans;
}
scRpc::RpcClient::~RpcClient() {
	
}